module arm.rcc;
/**
* 实现 stm32f4 的时钟控制器操作基础函数
* 片内缓冲
* 时钟源选择
* 时钟分频
* 电源参数配置
*See_Also: https://www.cnblogs.com/jiangyiming/p/15790850.html
*/

import chip;

//import arm.misc;

struct RCC_ClockType
{
    uint mSysFreq;      ///< 系统时钟频率
    uint mAHBFreq;      ///< AHB总线时钟频率
    uint mAPB1Freq;     ///< APB1总线时钟频率
    uint mAPB2Freq;     ///< APB2总线时钟频率

    uint SYSCLK_Frequency;      ///< 系统时钟频率
    uint HCLK_Frequency;        ///< AHB总线时钟频率
    uint PCLK1_Frequency;       ///< APB1总线时钟频率
    uint PCLK2_Frequency;       ///< APB2总线时钟频率
}



/// 判断 是否声明了 RCC 寄存器
private enum bool hasBusPeriph = HasPeriph!("RCC");
static if(hasBusPeriph):

import arm.bus;
import std.traits:hasMember;

//import arm.c.rcc_struct;

//enum fff = __traits(getMember,RCC_Type,"mask_CR");

//pragma(msg,"as:",__traits(allMembers,RCC_Type));
//pragma(msg,"L35:",__traits(getAttributes, RCC_Type.CR));
//pragma(msg,"L33333:",__traits(getMember,__traits(getMember,Peripherals.RCC,"mask_CR"),"PLLI2SRDY"));


//final abstract class RCC //: MMIOBus!(Peripherals.RCC)

static interface RCC
{

    //alias MMIO_CR = MMIOBus!(Peripherals.RCC.CR);
    enum RCC_Type* mBase = Peripherals.RCC;
    enum IFName = "RCC";
    //enum rcn = __traits(getMember,mBase,"CR");
    ///enum rcn = __traits(getMember,mBase,"CR");
    //enum rcc = __traits(getMember,typeof(rcn),"CR");
    
    //pragma(msg,"L:",typeof(*mBase));
    //alias PWREN = MMIO_APB1ENR!("PWREN",1);
    //static void PWREN(bool sel) => MMIO_APB1ENR!("PWREN")(sel?1:0);

    /// 系统时钟源选择状态
    enum FlagSWS
    {
        HSI = 0,        ///< HSI 内置`HSI RC`作为系统时钟源
        HSE = 1,        ///< HSE 外部HSE作为系统时钟源
        PLL = 2,        ///< PLL 锁相环作为系统时钟源
    }
    /// 系统时钟选择标志
    alias FlagSW = FlagSWS;
    /// AHB分频表
    enum size_t[16] FlagAHBPrescTable = [0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9];
    /// APB分频表

    enum FlagABPDiv
    {
        /// 无效
        DIV_1 = 0,
        DIV_2 = 0b100,      ///< 除以2
        DIV_4 = 0b101,      ///< 除以4
        DIV_8 = 0b110,      ///< 除以8
        DIV_16 = 0b111,     ///< 除以16
    }
    
    enum FlagAHBDiv
    {
        /// 无效
        DIV_1 = 0,
        DIV_2 = 0b1000,      ///< 除以2
        DIV_4 = 0b1001,      ///< 除以4
        DIV_8 = 0b1010,      ///< 除以8
        DIV_16 = 0b1011,     ///< 除以16
        DIV_64 = 0b1100,     ///< 除以64
        DIV_128 = 0b1101,    ///< 除以128
        DIV_256 = 0b1110,    ///< 除以256
        DIV_512 = 0b1111,    ///< 除以512
    }

    private{
        pragma(crt_constructor,CrtPriority!(FlagCrt.RCC,0))
        static this()
        {
            enum UseUSB = GetConfig!("UseUSB");
            enum uint targetFreq = GetConfig!("HCLK_Frequency");
            /// 初始化时钟设置,计算时钟配置
            enum ClickConfig mClock = CalcClockConfig(targetFreq,UseUSB);
            static assert(mClock.hclk == targetFreq,"Clock config error!");
            //pragma(msg,mClock.toPLLCFG());
            pragma(msg,"Clock ==>");
            pragma(msg,"Target Freq:",mClock.hclk);
            pragma(msg,"PLLM:",mClock.pllM);
            pragma(msg,"PLLN:",mClock.pllN);
            pragma(msg,"PLLP:",mClock.pllP);
            pragma(msg,"PLLQ:",mClock.pllQ);
            pragma(msg,"PLLI2S:",mClock.pllI2S);
            //pragma(msg,"A:",mClock.toPLLCFG());
            pragma(msg,"<== Clock");
            /// 设定修正值

            
            if(mClock.pllSrc){

            }else{
                /// 开启HSIRC电源
                mBase.CR.HSION(1);                
            }
            /// 等待稳定
            while(!mBase.CR.HSIRDY){}
            //uint tta = mClock.toPLLCFG();
            //mBase.PLLCFGR = tta;
            //mBase.PLLCFGR = 0x24001508;
            /**
            * 原有寄存器有一些位定义在手册上未体现,需要写原始位值回去才行
            */

            auto mPLLCFGR = mBase.PLLCFGR;
            mPLLCFGR.PLLM = mClock.pllM;
            mPLLCFGR.PLLN = mClock.pllN;
            mPLLCFGR.PLLP = mClock.pllP;
            mPLLCFGR.PLLQ = mClock.pllQ;
            mPLLCFGR.PLLSRC = mClock.pllSrc;
            uint vv1 = mPLLCFGR;
            mBase.PLLCFGR = vv1;

            mBase.CR.PLLON(1);                      /// 启动PLL
            /// 等待pll稳定
            while(!mBase.CR.PLLRDY){}
            auto mCFGR = mBase.CFGR;

            mCFGR.HPRE = GetConfig!("AHB_Prescaler");
            mCFGR.PPRE1 = GetConfig!("APB1_Prescaler");
            mCFGR.PPRE2 = GetConfig!("APB2_Prescaler");
            /// 切换时钟源
            mCFGR.SW = mClock.SystemClockSource;

            mBase.CFGR = mCFGR;
            /// 等待切换完成
            auto mSws = mBase.CFGR.SWS();
            //while(mBase.CFGR.SWS() != mClock.SystemClockSource){}
            do{
                mSws = mBase.CFGR.SWS();
            }while(mSws != mClock.SystemClockSource);
            /// 设置定时器预分频
            mBase.DCKCFGR.TIMPRE(0);
                
        }
    }

    
    /**
    * 使能或关闭指定的外设时钟
    * Params:
    *   name = 外设名称,字符串形式
    *   sel = 使能或关闭 0:禁用 1:使能
    * Example:
    ---
    RCC.Power!("GPIOAEN")(1);
    ---
    */
    static void Power(string name)(uint sel) @property 
    if(hasMember!(mBase.mask_APB1ENR,name))    /// APB1ENR 重载
    { 
        mBase.APB1ENR.opDispatch!name(sel);
    }

    static uint Power(string name)() @property 
    if(hasMember!(mBase.mask_APB1ENR,name))    /// APB1ENR 重载
    { 
        return mBase.APB1ENR.opDispatch!name();
    }

    static void Power(string name)(uint sel) @property 
    if(hasMember!(mBase.mask_APB2ENR,name))    /// APB2ENR 重载
    { 
        mBase.APB2ENR.opDispatch!name(sel);
    }

    static uint Power(string name)() @property 
    if(hasMember!(mBase.mask_APB2ENR,name))    /// APB2ENR 重载
    { 
        return mBase.APB2ENR.opDispatch!name();
    }

    static void Power(string name)(uint sel) @property
    if(hasMember!(mBase.mask_AHB1ENR,name))        /// AHB1ENR 重载
    {
        mBase.AHB1ENR.opDispatch!name(sel);
    }

    static uint Power(string name)() @property
    if(hasMember!(mBase.mask_AHB1ENR,name))        /// AHB1ENR 重载
    {
        return mBase.AHB1ENR.opDispatch!name();
    }

    static bool HSIReady()
    {
        return mBase.CR.HSIRDY() != 0;

    }

    /// 设置主频
    static void SetMainFreq(uint freq)
    {
        while(!HSIReady())    /// 等待稳定
        {
        }
        // 设定分频

    }

    static void Init()
    {
        //FlagSWS mSws = cast(FlagSWS)MMIO_Bitfield_Get!("CFGR",mBase.mask_CFGR.SWS)();
        FlagSWS mSws = cast(FlagSWS)mBase.CFGR.SWS();
    }
    /// 获取系统时钟频率
    static uint GetSystemClockFreq()
    out(r;r)
    {
        uint frequency = 0;
        //FlagSWS mSws = cast(FlagSWS)mBase.CFGR.SWS();
        auto mSws = cast(FlagSWS)(mBase.CFGR.SWS());               /// 获取系统时钟源
        //FlagSWS mSws = cast(FlagSWS)(mBase.CFGR.opDispatch!("SWS"));
        switch(mSws)
        {
            case FlagSWS.HSI: frequency = HSI_RC_Frequency;   //返回芯片内置时钟定义
                break;
            case FlagSWS.HSE: frequency = GetConfig!("HSE_Frequency");
                break;
            case FlagSWS.PLL: frequency = GetConfig!("HCLK_Frequency");
                break;
            default:
                break;
        }
        return frequency;
    }
    /// 获取HCLK 频率
    static FlagAHBDiv GetHCLKClockFreq()
    {
        //return FlagAHBPrescTable[mBase.CFGR.HPRE];
        return cast(FlagAHBDiv)mBase.CFGR.HPRE;

    }
    /// 获取PCLK 频率
    static FlagABPDiv GetPCLK1ClockFreq()
    {
        return cast(FlagABPDiv)mBase.CFGR.PPRE1;
    }
    /// 获取PCLK2 频率
    static FlagABPDiv GetPCLK2ClockFreq()
    {
        /// 获取分频器寄存器
        return cast(FlagABPDiv)mBase.CFGR.PPRE2;
    }

    enum FlagMCOClk1
    {
        HSI = 0b00,
        LSE = 0b01,
        HSE = 0b10,
        PLL = 0b11,
    }
    enum FlagMCOClk2
    {
        SYSCLK = 0b00,
        PLLI2S = 0b01,
        HSE = 0b10,
        PLL = 0b11,
    }
    enum FlagMCOPresc
    {
        Div_1 = 0b000,
        Div_2 = 0b100,
        Div_3 = 0b101,
        Div_4 = 0b110,
        Div_5 = 0b111,
    }
    /**
    * 配置时钟输出
    * todo: 执行后不能操作对应端口IO的配置 
    */
    static void ConfigMCO1(FlagMCOPresc pre,FlagMCOClk1 src)
    {
        mBase.CFGR.MCO1(src);
        mBase.CFGR.MCO1PRE(pre);        
    }


    
}

/**
* 时钟计算结果结构体
*/
struct ClickConfig
{
    uint hclk;  /// 计算后的主频 , 与原始相不同属于近似值
    
    uint pllM; /// PLLM系数
    uint pllN; /// PLLN系数
    uint pllP; /// PLLP系数
    uint pllQ; /// PLLQ系数
    

    bool pllSrc; /// PLL时钟源,外部时钟源为true,内部时钟源为false
    uint pllI2S; /// PLLI2S系数
    uint toPLLCFG()
    {
        return (pllM << 0) | (pllN << 6) | (pllP << 16) | (pllSrc << 22) | (pllQ << 24);

    }
    
    RCC.FlagSW SystemClockSource; /// 系统时钟源
}

/**
* 时钟`SYSCLK`参数计算函数,优先拆用外部时钟进行计算
*Params: 
*  freq = 目标主频
*  selectUSB = 是否计算USB时钟源
*/
ClickConfig CalcClockConfig(uint freq,bool selectUSB = false) pure
{
    ClickConfig config;
    enum HSEFreq = GetConfig!("HSE_Frequency");
    bool mfound = false;
    auto ClkSourceFreq = HSI_RC_Frequency;    /// 获取时钟源频率
    
    if(HSEFreq)
    {
        ClkSourceFreq = HSEFreq;
        config.pllSrc = true;
    }
    if(freq == ClkSourceFreq)
    {
        config.hclk = ClkSourceFreq;
        with ( RCC )
        {
            config.SystemClockSource = (config.pllSrc)?FlagSW.HSE:FlagSW.HSI;            
        }
        mfound = true;        
    }else{
        for(uint mpllM = ClockLimit.PLLM_MIN;mpllM < ClockLimit.PLLM_MAX;++mpllM)
        {   // PLLM
            auto vco_input = ClkSourceFreq / mpllM;
            if(vco_input < ClockLimit.PLLM_Frequency_Min || vco_input > ClockLimit.PLLM_Frequency_Max) continue;

            for(uint mpllN = ClockLimit.PLLN_MIN;mpllN < ClockLimit.PLLN_MAX;++mpllN)
            { // PLLN
                auto vco_output = vco_input * mpllN;
                if(vco_output < ClockLimit.PLLN_Frequency_Min || vco_output > ClockLimit.PLLN_Frequency_Max) continue;
                
                for(uint mpllP = ClockLimit.PLLP_MIN;mpllP < ClockLimit.PLLP_MAX;mpllP+=2)
                {
                    // PLLP
                    auto sysclk = vco_output / mpllP;
                    if(sysclk < ClockLimit.PLLP_Frequency_Min || sysclk > ClockLimit.PLLP_Frequency_Max) continue;

                    if(selectUSB){
                        
                        for(uint mpllQ = ClockLimit.PLLQ_MIN;mpllQ < ClockLimit.PLLQ_MAX;++mpllQ)
                        {
                            auto usbclk = vco_output / mpllQ;
                            if(usbclk < ClockLimit.PLLQ_Frequency_Min || usbclk > ClockLimit.PLLQ_Frequency_Max) continue;

                            if(usbclk < ClockLimit.USB_Frequency_Min || usbclk > ClockLimit.USB_Frequency_Max) continue;
                            if(usbclk != ClockLimit.USB_Frequency) continue;
                            if(sysclk == freq) 
                            {
                                config.hclk = sysclk;
                                config.pllM = mpllM;
                                config.pllN = mpllN;
                                config.pllP = (mpllP-2)/2;
                                config.pllQ = mpllQ;
                                with ( RCC )
                                {
                                    config.SystemClockSource = FlagSW.PLL;
                                }
                                mfound = true;
                                break;
                            }
                        }
                        
                    }else{
                        if(sysclk == freq)
                        {
                            config.hclk = sysclk;
                            config.pllM = mpllM;
                            config.pllN = mpllN;
                            config.pllP = (mpllP - 2)/2;
                            with ( RCC )
                            {
                                config.SystemClockSource = FlagSW.PLL;
                            }
                            mfound = true;
                            break;
                        }
                    }
                    if(mfound)
                    {
                        break;
                    }
                }
                if(mfound)
                {
                    break;
                }
            }
        }

    }

    return config;
}

/// 获取时钟源频率
enum SystemClockFreq = GetConfig!("HCLK_Frequency");


/+++
version(NONE):

/// AHB2ENR 使能位
enum RCC_AHB2ENR
{
    OTGFSEN = 7,    ///< OTGFS时钟使能位
}


/// 振荡器源配置标志
enum OscillatorSourceFlag : ubyte
{
    Les_Off = 0,                ///< 关闭低速外部振荡器
    Les_Lsi = 1,                ///< 低速内部振荡器作为低速外部振荡器
    Les_Lse = 2,                ///< 低速外部振荡器作为低速外部振荡器
}

private {
    /// 插入寄存器位域
    
    /// 默认HSI校准微调值
    enum DefaultHSICalibrationValue = 16;
    /// CFGR 功能标志
    enum CfgrFlag : ubyte
    {
        MCO2_SysClk = 0,            ///< MCO2输出系统时钟
        MCO2_PLLI2S = 1,            ///< MCO2输出PLLI2S时钟
        MCO2_HSE = 2,               ///< MCO2输出HSE时钟
        MCO2_PLL = 3,               ///< MCO2输出PLL时钟

        SWS_HSI = 0,                ///< 系统时钟源为HSI
        SWS_HSE = 1,                ///< 系统时钟源为HSE
        SWS_PLL = 2,                ///< 系统时钟源为PLL

    }
    /// MCO分频系数标志
    enum MCODivFlag : ubyte
    {
        Div1 = 0,                   ///< MCO分频系数为1
        Div2 = 4,                   ///< MCO分频系数为2
        Div3 = 5,                   ///< MCO分频系数为3
        Div4 = 6,                   ///< MCO分频系数为4
        Div5 = 7,                   ///< MCO分频系数为5
    }

}

enum Oscillator : ubyte
{
    None = 0,
    Hse = 1 << 0,
    Hsi = 1 << 1,
    Lse = 1 << 2,
    Lsi = 1 << 3,
}

struct PLLInitTypeDef
{
    uint PLLState;
    uint PLLSource;
    uint PLLM;
    uint PLLN;
    uint PLLP;
    uint PLLQ;
    uint PLLR;
}

struct OscInitTypeDef
{
    Oscillator OscillatorType;            ///< The oscillators to be configured.
    uint HSEState;                  ///< The new state of the HSE.
    uint LSEState;                  ///< The new state of the LSE.
    uint HSIState;                  ///< The new state of the HSI.
    uint HSICalibrationValue;       ///< HSI校准微调 .
    uint LSIState;                  ///< The new state of the LSI.
    PLLInitTypeDef PLL;             ///< PLL structure parameters.
}


struct sCFGR
{
    align(1):
    uint SW : 2;            ///< 系统时钟选择 
    uint SWS : 2;           ///< 系统时钟选择状态
    uint HPRE : 4;          ///< AHB时钟分频系数
    uint :2;
    uint PPRE1 : 3;         ///< APB1时钟分频系数
    uint PPRE2 : 3;         ///< APB2时钟分频系数
    uint RTCPRE : 5;        ///< RTC时钟分频系数
    uint MCO1 : 2;          ///< MCO1时钟输出
    uint I2SSRC : 1;        ///< I2S时钟源选择
    uint MCO1PRE : 3;       ///< MCO1时钟分频系数
    uint MCO2PRE : 3;       ///< MCO2时钟分频系数
    uint MCO2 : 2;          ///< MCO2时钟输出
    ref T opCast(T:uint)() const
    {
        return *cast(T*)&this;
    }
}
debug static assert(sCFGR.sizeof == uint.sizeof);



final abstract class D_RCC
{
    /**
    * 晶振配置
    */
    Hal_Status OscConfig(ref OscInitTypeDef  osc)
    {
        //if(osc is null) return Hal_Status.Error;
        //Cfgr_Struct3 mCfgr = cast(Cfgr_Struct33)RCC.CFGR.value();
        //Cfgr_Struct mCfgr = *cast(Cfgr_Struct*)&(ReadMemory!(RCC.CFGR.RegistersAddress,uint)());
        //if(Cfgr.SWS & )
        auto mCfgr = RegMap!(RCC.CFGR.RegistersAddress,sCFGR);

        if(
            (mCfgr.SWS == CfgrFlag.SWS_HSE) ||
            ((mCfgr.SWS == CfgrFlag.SWS_PLL) && (RCC.PLLCFGR.PLLSRC.val()))
            )
        {

        }else{

        }

        return Hal_Status.None;
    }


    /// 获取cfgR寄存器值
    @property
    pragma(inline,_inline)
    static sCFGR Reg_CFGR()
    {
        return cast(sCFGR)ReadMemory!(RCC.CFGR.RegistersAddress,uint)();
    }
    @property
    pragma(inline,_inline)
    static void Reg_CFGR(sCFGR mCfgr)
    {
        WriteMemory!(RCC.CFGR.RegistersAddress,uint)(cast(uint)mCfgr);
    }
    

    /**
    * AHB1ENR 寄存器操作模板
    */
    pragma(inline,_inline)
    static void AHB1ENR_Set(RCC_AHB1ENR mPos)() if(mPos < 32)
    {
        return setBit!(RCC.AHB1ENR.RegistersAddress,mPos,uint);
    }
    pragma(inline,_inline)
    static void AHB1ENR_Clear(RCC_AHB1ENR mPos)() if(mPos < 32)
    {
        return clearBit!(RCC.AHB1ENR.RegistersAddress,mPos,uint);
    }
    pragma(inline,_inline)
    static bool AHB1ENR_Get(RCC_AHB1ENR mPos)() if(mPos < 32)
    {
        return hasBit!(RCC.AHB1ENR.RegistersAddress,mPos,uint);
    }
    /**
    * APB1ENR 寄存器操作模板
    */
    pragma(inline,_inline)
    static void APB1ENR_Set(RCC_APB1ENR mPos)() if(mPos < 32)
    {
        return setBit!(RCC.APB1ENR.RegistersAddress,mPos,uint);
    }
    pragma(inline,_inline)
    static void APB1ENR_Clear(RCC_APB1ENR mPos)() if(mPos < 32)
    {
        return clearBit!(RCC.APB1ENR.RegistersAddress,mPos,uint);
    }
    pragma(inline,_inline)
    static bool APB1ENR_Get(RCC_APB1ENR mPos)() if(mPos < 32)
    {
        return hasBit!(RCC.APB1ENR.RegistersAddress,mPos,uint);
    }
    
    alias PWREN_Enable = APB1ENR_Set!(RCC_APB1ENR.PWREN);       /// 电源接口时钟使能
    alias PWREN_Disable = APB1ENR_Clear!(RCC_APB1ENR.PWREN);    /// 电源接口时钟禁用
    alias PWREN_IsEnable = APB1ENR_Get!(RCC_APB1ENR.PWREN);     /// 电源接口时钟状态

    alias I2C3EN_Enable = APB1ENR_Set!(RCC_APB1ENR.I2C3EN);       /// I2C3时钟使能
    alias I2C3EN_Disable = APB1ENR_Clear!(RCC_APB1ENR.I2C3EN);    /// I2C3时钟禁用
    alias I2C3EN_IsEnable = APB1ENR_Get!(RCC_APB1ENR.I2C3EN);     /// I2C3时钟状态

    

    
    /**
    * GPIO 时钟使能
    */
    pragma(inline,_inline)
    static void GPIOEnable(alias port)() if(__traits(isFinalClass,port))
    {
        mixin("RCC.AHB1ENR."~port.stringof~"EN.val(true);");
        //Peripherals.RCC.mask_AHB1ENR.GPIOAEN
    }
    /**
    * GPIO 时钟禁用
    */
    pragma(inline,_inline)
    static void GPIODisable(alias port)() if(__traits(isFinalClass,port))
    {
        mixin("RCC.AHB1ENR."~port.stringof~"EN.val(false);");
    }
    /**
    * GPIO 时钟状态
    */
    pragma(inline,_inline)
    static bool GPIOIsEnable(alias port)() if(__traits(isFinalClass,port))
    {
        return mixin("RCC.AHB1ENR."~port.stringof~"EN.val()");
    }

    /**
        使能 Pll
    */
    void Pll_Enable()
    {
        RCC.CR.PLLON.val = true;
    }
    /**
        禁用 Pll
    */
    void Pll_Disable()
    {
        RCC.CR.PLLON.val = false;
    }
    
    /**
        检查 Pll 是否可用
    */
    bool Pll_Is_Ready()
    {
        return RCC.CR.PLLRDY.val();
    }



}+++/